package com.iotechn.uninotify.controller;

import com.alibaba.fastjson.JSONObject;
import com.iotechn.uninotify.Const;
import com.iotechn.uninotify.annotation.HttpMethod;
import com.iotechn.uninotify.annotation.HttpParam;
import com.iotechn.uninotify.annotation.HttpParamType;
import com.iotechn.uninotify.annotation.param.NotNull;
import com.iotechn.uninotify.service.biz.DeveloperBizService;
import com.iotechn.uninotify.domain.DeveloperDO;
import com.iotechn.uninotify.exception.NotifyExceptionDefinition;
import com.iotechn.uninotify.exception.NotifyServiceException;
import com.iotechn.uninotify.exception.ServiceException;
import com.iotechn.uninotify.manager.ApiManager;
import com.iotechn.uninotify.model.GatewayResponse;
import com.iotechn.uninotify.util.RequestUtil;
import com.iotechn.uninotify.util.SHAUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.*;
import java.net.URLEncoder;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: rize
 * Date: 2019/12/26
 * Time: 14:42
 */
@RequestMapping("/m.api")
@Controller
public class ApiController {

    private static final Logger logger = LoggerFactory.getLogger(ApiController.class);


    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private DeveloperBizService developerBizService;

    @Value("${com.iotechn.uninotify.env}")
    private String ENV;

    @RequestMapping(method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String invoke(HttpServletRequest req, HttpServletResponse res) {
        long invokeTime = System.currentTimeMillis();
        try {
            logger.info("[用户请求] request=" + JSONObject.toJSONString(req.getParameterMap()));
            Object obj = process(req, res, invokeTime);
            if(Const.IGNORE_PARAM_LIST.contains(obj.getClass())){
                return obj.toString();
            }
            String result = JSONObject.toJSONString(obj);
            long during = System.currentTimeMillis() - invokeTime;
            logger.info("[用户请求] 用时 " + during + "ms, response=" + JSONObject.toJSONString(result));
            return result;
        } catch (ServiceException e) {
            GatewayResponse gatewayResponse = new GatewayResponse();
            gatewayResponse.setTimestamp(invokeTime);
            gatewayResponse.setErrno(e.getCode());
            gatewayResponse.setErrmsg(e.getMessage());
            String result = JSONObject.toJSONString(gatewayResponse);
            long during = System.currentTimeMillis() - invokeTime;
            logger.info("[用户请求] 用时 " + during + "ms, response=" + JSONObject.toJSONString(result));
            return result;
        }
    }


    private Object process(HttpServletRequest request, HttpServletResponse response, long invokeTime) throws ServiceException {
        try {
            ApiManager apiManager = applicationContext.getBean(ApiManager.class);
            Map<String, String[]> parameterMap = request.getParameterMap();
            String[] gps = parameterMap.get("_gp");
            String[] mts = parameterMap.get("_mt");
            String[] timestamp = parameterMap.get("timestamp");
            if(gps == null  || mts == null || gps.length == 0 || mts.length == 0){
                throw new NotifyServiceException(NotifyExceptionDefinition.NOTIFY_API_NOT_EXISTS);
            }
            if (timestamp == null || timestamp.length == 0 || (System.currentTimeMillis() - new Long(timestamp[0])) > 1000l * 5 * 60) {
                throw new NotifyServiceException(NotifyExceptionDefinition.NOTIFY_REQUEST_EXPIRED);
            }
            String _gp = gps[0];
            String _mt = mts[0];
            String[] _types = parameterMap.get("_type");
            String _type = null;
            if (_types != null && _types.length > 0) {
                _type = _types[0];
            }
            //校验签名
            checkSign(request);
            Method method = apiManager.getMethod(_gp, _mt);
            if (method == null) {
                throw new NotifyServiceException(NotifyExceptionDefinition.NOTIFY_API_NOT_EXISTS);
            }
            HttpMethod httpMethod = method.getAnnotation(HttpMethod.class);
            if (httpMethod == null) {
                //只起标记作用防止调到封闭方法了
                throw new NotifyServiceException(NotifyExceptionDefinition.NOTIFY_API_NOT_EXISTS);
            }
            Object serviceBean = applicationContext.getBean(method.getDeclaringClass());
            Parameter[] methodParameters = method.getParameters();
            Object[] args = new Object[methodParameters.length];
            for (int i = 0; i < methodParameters.length; i++) {
                Parameter methodParam = methodParameters[i];
                HttpParam httpParam = methodParam.getAnnotation(HttpParam.class);
                if (httpParam == null) {
                    throw new NotifyServiceException(NotifyExceptionDefinition.NOTIFY_API_NOT_EXISTS);
                }
                if (httpParam.type() == HttpParamType.COMMON) {
                    String[] paramArray = parameterMap.get(httpParam.name());
                    if (paramArray != null && paramArray.length > 0 && !StringUtils.isEmpty(paramArray[0])) {
                        Class<?> type = methodParam.getType();
                        //参数校验
                        checkParam(type, methodParam, paramArray[0]);
                        if (String.class == type) {
                            args[i] = paramArray[0];
                        } else if (Const.IGNORE_PARAM_LIST.contains(type)) {
                            Constructor<?> constructor = type.getConstructor(String.class);
                            args[i] = constructor.newInstance(paramArray[0]);
                        } else if (type.isArray()) {
                            //若是数组
                            Class<?> itemType = type.getComponentType();
                            Object realType[] = (Object[]) Array.newInstance(itemType,paramArray.length);
                            if(paramArray.length > 0){
                                for(int j = 0; j < paramArray.length; j++) {
                                    if(Const.IGNORE_PARAM_LIST.contains(itemType)){
                                        Constructor<?> constructor = itemType.getConstructor(String.class);
                                        realType[j] = constructor.newInstance(paramArray[j]);
                                    }else {
                                        realType[j] = JSONObject.parseObject(paramArray[j], itemType);
                                    }
                                }
                            }
                            args[i] = realType;
                        } else {
                            //Json解析
                            args[i] = JSONObject.parseObject(paramArray[0], type);
                        }
                    } else {
                        if (!StringUtils.isEmpty(httpParam.valueDef())) {
                            //若有默认值
                            Class<?> type = methodParam.getType();
                            Constructor<?> constructor = type.getConstructor(String.class);
                            args[i] = constructor.newInstance(httpParam.valueDef());
                        } else {
                            if (methodParam.getAnnotation(NotNull.class) != null) {
                                logger.error("missing :" + httpParam.name());
                                throw new NotifyServiceException(NotifyExceptionDefinition.NOTIFY_PARAM_CHECK_FAILED);
                            }
                            args[i] = null;
                        }
                    }
                } else if (httpParam.type() == HttpParamType.IP) {
                    //这里根据实际情况来定。 若使用了负载均衡，Ip将会被代理服务器设置到某个Header里面
                    if (ENV.equals("1")) {
                        //若是开发环境
                        args[i] = "27.10.60.71";
                    } else {
                        args[i] = request.getHeader("X-Forwarded-For");
                    }
                }else if (httpParam.type() == HttpParamType.HEADER) {
                    String header = request.getHeader(httpParam.name());
                    args[i] = header;
                    if (header == null && methodParam.getAnnotation(NotNull.class) != null) {
                        throw new NotifyServiceException(NotifyExceptionDefinition.NOTIFY_PARAM_CHECK_FAILED);
                    }
                }
            }

            Object invokeObj = method.invoke(serviceBean, args);
            if (!StringUtils.isEmpty(_type) && "raw".equals(_type)) {
                //如果是不用包装的直接返回
                return invokeObj;
            }
            GatewayResponse gatewayResponse = new GatewayResponse();
            gatewayResponse.setErrno(200);
            gatewayResponse.setErrmsg("成功");
            gatewayResponse.setTimestamp(invokeTime);
            gatewayResponse.setData(invokeObj);
            return gatewayResponse;
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            if (e instanceof InvocationTargetException) {
                InvocationTargetException proxy = (InvocationTargetException) e;
                Throwable targetException = proxy.getTargetException();
                if (targetException instanceof ServiceException) {
                    throw (ServiceException) targetException;
                }
            }
            logger.error("[网关] 系统未知异常", e);
            throw new NotifyServiceException(NotifyExceptionDefinition.NOTIFY_UNKNOWN_EXCEPTION);
        }
    }

    private void checkSign(HttpServletRequest request) throws ServiceException {
        String sign = request.getParameter("sign");
        if (StringUtils.isEmpty(sign)) {
            throw new NotifyServiceException(NotifyExceptionDefinition.NOTIFY_SIGN_CANNOT_BE_NULL);
        }
        String appId = request.getParameter("appId");
        if (StringUtils.isEmpty(appId)) {
            throw new NotifyServiceException(NotifyExceptionDefinition.NOTIFY_APP_ID_CANNOT_BE_NULL);
        }
        DeveloperDO developerDO = developerBizService.getDeveloperByAppId(appId);
        RequestUtil.setDeveloper(developerDO);
        //得到排序后的明文
        String raw = request.getParameterMap().values().stream().map(item -> {
            //签名本身无法加入签名算法参数中, 而appSecret 需要加入签名中。所以遇到签名时，将其替换为appSecert即可
            if (sign.equals(item[0])) {
                return developerDO.getAppSecret();
            } else {
                return item[0];
            }
        }).sorted().collect(Collectors.joining());
        String res = null;
        try {
            res = SHAUtil.shaEncode(URLEncoder.encode(raw, "utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        if (!sign.equals(res)) {
            throw new NotifyServiceException(NotifyExceptionDefinition.NOTIFY_SIGN_NOT_INCORRECT);
        }
    }


    private void checkParam(Class<?> type, Parameter methodParam, String target) throws ServiceException {
    }

}
